#include <shm.h>         //使用了shm_t这个结构体
#include <sys/types.h>   //使用了key_t的定义
#include <linux/mm.h>    //使用了PAGE_SIZE, get_free_page(), put_page()
#include <linux/sched.h> //使用了get_base宏函数
#include <errno.h>

static shm_t shm_list[SHM_NUM]; // 静态变量，下一次调用的时候保持原来的赋值；

/* 新建/打开一页内存 */
int sys_shmget(key_t key, size_t size)
{
    void *address; // 指向空闲页面的首地址;
    int i;
    /* 如果曾经分配过，直接返回下标 */
    for (i = 0; i < SHM_NUM; i++)
    {
        if (shm_list[i].key == key)
            return i;
    }
    if (size > PAGE_SIZE)
        return -EINVAL;
    address = get_free_page();
    if (!address)
        return -ENOMEM;

    /* 找到1个可用的共享内存 */
    for (i = 0; i < SHM_NUM; i++)
    {
        if (shm_list[i].key == 0)
        {
            shm_list[i].key = key;
            shm_list[i].size = size;
            shm_list[i].address = address;
            return i;
        }
    }
    return -ENOMEM;
}

/* 将 shmid 指定的共享页面映射到当前进程的虚拟地址空间中，并将其首地址返回 */
void *sys_shmat(int shmid)
{
    void *linear_address;
    if (shmid < 0)
        return -EINVAL;

    /* 寻找空闲的虚拟地址空间 */
    linear_address = get_base(current->ldt[2]) + current->brk;
    current->brk += PAGE_SIZE;
    if (shm_list[shmid].key != 0)
    {
        put_page(shm_list[shmid].address, linear_address); // 建立线性地址和物理地址的映射
        incr_mem_map(shm_list[shmid].address);
        return current->brk - PAGE_SIZE;
    }
    return -EINVAL;
}
